package com.zto.sch.base.proxy;

import org.springframework.util.StringUtils;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

public class ProxyUtil {
    private static Integer num = 0;

    /**
     * 生成类 content
     * jvm转译成class文件
     * 利用classLoader创建对象
     *
     * @return
     */
    public static Object instance(Class targetClazz, MyDIYInvocationHandler h) throws Exception {
//        Class targetClazz = target.getClass().getInterfaces()[0];
        //1,生成类 content
        String content = "";
        String lineSeperator = System.getProperty("line.separator");
        String tabSeperator = "\t";
        String simpleClassName = targetClazz.getInterfaces()[0].getSimpleName();
        Method[] methods = targetClazz.getDeclaredMethods();

        String packageContent = "package com.google;" + lineSeperator;
        String importContent = "import " + targetClazz.getName() + ";" + lineSeperator
                + "import com.zto.zzt.proxy.dynamic.diy.my.MyDIYInvocationHandler;" + lineSeperator
                + "import java.lang.reflect.Method;" + lineSeperator;

        String firstLine = "public class $Proxy" + num + " implements " + simpleClassName + "{" + lineSeperator;
        String filedContent = tabSeperator + "private MyDIYInvocationHandler h;" + lineSeperator;
        String constructorContent = tabSeperator + "public $Proxy" + num + "(MyDIYInvocationHandler h) {" + lineSeperator
                + tabSeperator + tabSeperator + "this.h = h;" + lineSeperator
                + tabSeperator + "}" + lineSeperator;
        String methodsContent = "";
        for (Method method : methods) {
            String returnTypeName = method.getReturnType().getSimpleName();
            String methodName = method.getName();
            Class<?>[] args = method.getParameterTypes();
            String argsContent = "";
            String paramtersContent = "";
            //class类型，invoke方法传参使用
            String argsTypeConten = "";
            for (int i = 0; i < args.length; i++) {
                String argSimpleName = args[i].getSimpleName();
                argsContent += argSimpleName + " p" + i + ",";
                paramtersContent += "p" + i + ",";
                argsTypeConten += argSimpleName + ".class,";
            }

            if (argsContent.length() > 1) {
                argsContent = argsContent.substring(0, (argsContent.length() - 1));
                paramtersContent = paramtersContent.substring(0, (paramtersContent.length() - 1));
                argsTypeConten = argsTypeConten.substring(0, (argsTypeConten.length() - 1));
            }

            String mContent = "";

            //注意:这个要动态获取invoke方法的method入参。仔细品品
            if (StringUtils.isEmpty(argsContent)) {
                mContent = tabSeperator + tabSeperator + "Method m = Class.forName(\"" + targetClazz.getName() + "\").getDeclaredMethod(\"" + methodName + "\");";
            } else {
                mContent = tabSeperator + tabSeperator + "Method m = Class.forName(\"" + targetClazz.getName() + "\")" +
                        ".getDeclaredMethod(\"" + methodName + "\"," + argsTypeConten + ");";
            }
            String argsContet = lineSeperator + tabSeperator + tabSeperator + "Object[] args = new Object[]{" + paramtersContent + "};";
            String returnContent = "";
            //动态生成返回值
            if ("void".equals(returnTypeName)) {
                returnContent += lineSeperator + tabSeperator + tabSeperator + "h.invoke(m,args);";
            } else {
                returnContent += lineSeperator + tabSeperator + tabSeperator + "return (" + returnTypeName + ")h.invoke(m,args);";
            }

            methodsContent += tabSeperator + "public " + returnTypeName + " " + methodName + "(" + argsContent + ") throws Exception" + "{"
                    + lineSeperator + mContent + argsContet
                    + returnContent;

            methodsContent += lineSeperator + tabSeperator + "}" + lineSeperator;
        }
        content = packageContent + importContent + firstLine + filedContent + constructorContent + methodsContent + "}";

        //2，生成java文件
        String dir = "/Users/sch/mydocument/temp/com/google/";
        String fileName = "$Proxy" + num + ".java";
        File fileDir = new File(dir);
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }

        FileWriter fileWriter = new FileWriter(new File(dir, fileName));
        fileWriter.write(content);
        fileWriter.flush();
        fileWriter.close();


        //3,jvm转译成class文件
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        Iterable units = fileMgr.getJavaFileObjects(new File(dir, fileName));

        JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
        t.call();
        fileMgr.close();


        //4,利用classLoader创建对象：URLClassLoader可以加载任意位置文件
        URL url = new URL("file:/Users/sch/mydocument/temp/");
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
        Class<?> aClass = urlClassLoader.loadClass("com.google.$Proxy" + num);
        Constructor<?> constructor = aClass.getConstructor(MyDIYInvocationHandler.class);
        num++;
        return constructor.newInstance(h);
    }
}